package com.lnj.product.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.IndexRequest;
import co.elastic.clients.elasticsearch.core.IndexResponse;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lnj.api.FileUploadApi;
import com.lnj.api.IdGeneratorApi;
import com.lnj.bean.dto.CategoryDTO;
import com.lnj.bean.dto.ProductDTO;
import com.lnj.bean.model.Product;
import com.lnj.product.mapper.ProductMapper;
import com.lnj.product.service.CategoryService;
import com.lnj.product.service.ProductService;
import com.lnj.product.utils.JsonUtil;
import com.lnj.product.utils.KafkaProducterUtil;
import com.lnj.product.vo.ProductQueryVO;
import com.lnj.utils.RedisKeys;
import com.lnj.utils.ResponseResult;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * @program: shopping147_cloud
 * @description:
 * @author: zy
 * @create: 2025-07-24 19:32
 */
@Service
@CacheConfig(cacheNames = "productCache") // 定义缓存的名称，统一管理
@Slf4j
public class ProductServiceImpl  implements ProductService {
    //public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private IdGeneratorApi idGeneratorApi;
    @Autowired
    private FileUploadApi fileUploadApi;

    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private CategoryService categoryService;

    @Autowired
    private KafkaProducterUtil kafkaProducterUtil;//消息中间件生产者
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Autowired
    private Executor executor;
    @Autowired
    private JsonUtil jsonUtil;

    /**
     * 分页查询商品
     * @param queryVO 查询参数，包含商品名称、分类ID、商品状态、排序方式等
     * @return 商品DTO的分页结果
     */
    @Override
    public IPage<ProductDTO> listProductsPaged(ProductQueryVO queryVO) {
        // 创建分页对象
        Page<Product> page = new Page<>(queryVO.getCurrent(), queryVO.getSize());

        // 构建查询条件
        LambdaQueryWrapper<Product> queryWrapper = new LambdaQueryWrapper<>();

        // 根据商品名称模糊查询
        if (StringUtils.hasText(queryVO.getProductName())) {
            queryWrapper.like(Product::getProductName, queryVO.getProductName());
        }
        // 根据分类ID精确查询
        if (queryVO.getCategoryId() != null) {
            queryWrapper.eq(Product::getCategoryId, queryVO.getCategoryId());
        }
        // 根据商品状态查询
        if (queryVO.getProductStatus() != null) {
            queryWrapper.eq(Product::getProductStatus, queryVO.getProductStatus());
        }

        // 处理排序
        if (StringUtils.hasText(queryVO.getSortField())) {
            boolean isAsc = !"desc".equalsIgnoreCase(queryVO.getSortOrder());
            switch (queryVO.getSortField().toLowerCase()) {
                case "price":
                    queryWrapper.orderBy(true, isAsc, Product::getPrice);
                    break;
                case "productId":
                    queryWrapper.orderBy(true, isAsc, Product::getProductId);
                    break;
                // 可以添加其他排序字段
                default:
                    // 默认按商品ID降序
                    queryWrapper.orderByDesc(Product::getProductStatus);
                    break;
            }
        } else {
            // 默认排序
            queryWrapper.orderByDesc(Product::getProductId);
        }
        // 执行分页查询
        IPage<Product> productPage = productMapper.selectPage(page, queryWrapper);
        // 将实体分页结果转换为DTO分页结果

        return productPage.convert(product -> {
            ProductDTO dto = new ProductDTO();
            BeanUtils.copyProperties(product, dto);
            // 这里可以添加从其他服务获取分类名称的逻辑，例如通过Feign调用分类服务
            CategoryDTO categoryById = categoryService.getCategoryById(dto.getCategoryId());
            dto.setCategoryName(categoryById.getCategoryName());
            return dto;
        });
    }

    /**
     * 查询特价商品列表的实现
     * 特价商品定义为：当前价格低于原价，且商品状态为上架 (product_status = 1)
     * 结果按商品ID降序排列
     * @return 特价商品列表
     */
    @Override
    public IPage<ProductDTO> getSpecialOfferProducts( ProductQueryVO queryVO ) {
        // 创建分页对象
        Page<Product> page = new Page<>(queryVO.getCurrent(), queryVO.getSize());
        // 使用MyBatis-Plus的QueryWrapper构建查询条件
        QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
        // 注意：这里的"price"和"original_price"是数据库列名
        // 修正：使用 apply 方法进行列与列之间的比较，避免将列名作为参数
        queryWrapper.apply("price < originalprice") //    where price < original_price
                .eq("productstatus", 1)      // product_status = 1
                .orderByDesc("productid");   // order by product_id DESC

        // 执行分页查询
        IPage<Product> productPage = productMapper.selectPage(page, queryWrapper);
        // 将实体分页结果转换为DTO分页结果
        return productPage.convert(product -> {
            ProductDTO dto = new ProductDTO();
            BeanUtils.copyProperties(product, dto);
            // 这里可以添加从其他服务获取分类名称的逻辑，例如通过Feign调用分类服务
             dto.setCategoryName(categoryService.getCategoryById(product.getCategoryId()).getCategoryName());
            return dto;
        });
    }


    /**
     * 上架商品 (新增商品)
     * @param productDTO 待上架的商品信息
     * @return 上架成功后的商品DTO
     */
    @Override
    @Transactional // 开启事务
    @CacheEvict(allEntries = true)
    public ProductDTO addProduct(ProductDTO productDTO) {
        // 1. 生成商品ID
        ResponseResult rr= this.idGeneratorApi.getNextId();
        if(  rr.getCode()!=1){
            throw new RuntimeException("商品ID生成失败");
        }
        Long productId =Long.parseLong(rr.getData().toString());
        productDTO.setProductId(productId);

        //if (StringUtils.hasText(productDTO.getMainImage())) {
        ResponseResult rr2= this.fileUploadApi.upload(   new MultipartFile[]{ productDTO.getMainImageFile() });
        if (rr2.getCode() == 0) {
            // 部分成功 - data是拼接的字符串
            String resultData = rr2.getData().toString();
            // 解析出URL部分（假设格式为"url,url失败:xxx"）
            String[] parts = resultData.split("失败:");
            if (parts.length > 0 && !parts[0].isEmpty()) {
                String[] urls = parts[0].split(",");
                if (urls.length > 0) {
                    productDTO.setMainImage(urls[0]); // 取第一个URL
                }
            }
            // 抛出异常包含失败信息
            throw new RuntimeException("图片上传部分失败: " + (parts.length > 1 ? parts[1] : "未知原因"));
        } else if (rr2.getCode()==2){
            // 全部失败 - data是失败信息字符串
            throw new RuntimeException("图片上传失败: " + rr2.getData());
        }
        List<String> mainImages= (List<String>) rr2.getData();
        productDTO.setMainImage(   mainImages.get(0)  );
        // }
        //  if (StringUtils.hasText(productDTO.getSubImages())) {
        // 假设subImages是逗号分隔的图片数据，这里需要循环上传
        MultipartFile[] subImageFiles = productDTO.getSubImageFiles();
        ResponseResult rr3= this.fileUploadApi.upload(   subImageFiles );
        if (rr3.getCode() == 0) {
            // 部分成功 - data是拼接的字符串
            String resultData = rr2.getData().toString();
            // 解析出URL部分（假设格式为"url,url失败:xxx"）
            String[] parts = resultData.split("失败:");
            if (parts.length > 0 && !parts[0].isEmpty()) {
                String[] urls = parts[0].split(",");
                if (urls.length > 0) {
                    productDTO.setMainImage(urls[0]); // 取第一个URL
                }
            }
            // 抛出异常包含失败信息
            throw new RuntimeException("图片上传部分失败: " + (parts.length > 1 ? parts[1] : "未知原因"));
        } else if (rr2.getCode()==2){
            // 全部失败 - data是失败信息字符串
            throw new RuntimeException("图片上传失败: " + rr2.getData());
        }

        List<String> subImages= (List<String>) rr3.getData();
        StringBuilder uploadedSubImages = new StringBuilder();
        for (String imgData : subImages) {
            if (StringUtils.hasText(imgData)) {
                uploadedSubImages.append(imgData).append(",");
            }
        }
        if (uploadedSubImages.length() > 0) {
            uploadedSubImages.setLength(uploadedSubImages.length() - 1); // 移除最后一个逗号
        }
        productDTO.setSubImages(uploadedSubImages.toString());
        //  }
        // 3. 设置默认状态为上架
        if (productDTO.getProductStatus() == null) {
            productDTO.setProductStatus(1); // 默认上架
        }
        // 4. 确保价格不为空
        if (productDTO.getPrice() == null) {
            productDTO.setPrice(BigDecimal.ZERO);
        }
        // 5. 将DTO转换为实体
        Product product = new Product();
        BeanUtils.copyProperties(productDTO, product);
        product.setUpdateTime(LocalDateTime.now());
        // 6. 插入数据库
        int result = productMapper.insert(product);
        if (result > 0) {
            // 插入成功后，返回包含完整信息的DTO
            BeanUtils.copyProperties(product, productDTO);
            String json =  jsonUtil.toJson(product);
            //添加Redis缓存
            stringRedisTemplate.opsForValue().set(RedisKeys.PRODUCT_INFO+productId,json);
            //消息中间件
            kafkaProducterUtil.sendMessage("es_data_topic","products",json);
            return productDTO;
        } else {
            throw new RuntimeException("商品上架失败");
        }
    }

    /**
     * 下架商品
     * @param productId 商品ID
     * @return 是否下架成功
     */
    @Override
    @Transactional
    @CacheEvict(allEntries = true)
    public boolean delistProduct(Long productId) {
        // 第一次删除缓存
        String productKey = RedisKeys.PRODUCT_INFO + productId;
        stringRedisTemplate.delete(productKey);

        // 更新数据库状态
        LambdaUpdateWrapper<Product> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Product::getProductId, productId)
                .set(Product::getProductStatus, 0)// 0-下架
                .set(Product::getUpdateTime,LocalDateTime.now());

        int result = productMapper.update(null, updateWrapper);

        // 延迟双删：延迟一段时间后再次删除缓存
        if (result > 0) {
            // 延迟删除缓存，确保在并发情况下数据一致性
            executor.execute(() -> {
                try {
                    // 延迟1秒再次删除缓存
                    Thread.sleep(1000);
                    stringRedisTemplate.delete(productKey);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });

            // 同步到ES
            ProductDTO productDTO = getProductById(productId);
            Product product = new Product();
            BeanUtils.copyProperties(productDTO,product);
            product.setProductStatus(0);
            kafkaProducterUtil.sendMessage("es_data_topic","products", jsonUtil.toJson(product));
        }
        return result > 0;
    }

    /**
     * 更新商品信息 - 使用延迟双删保证数据一致性
     * @param productDTO 待更新的商品信息
     * @return 更新成功后的商品DTO
     */
    @Override
    @Transactional
    @CacheEvict(allEntries = true)
    public ProductDTO updateProduct(ProductDTO productDTO) {
        if (productDTO.getProductId() == null) {
            throw new IllegalArgumentException("商品ID不能为空，无法更新");
        }

        // 第一次删除缓存
        String productKey = RedisKeys.PRODUCT_INFO + productDTO.getProductId();
        stringRedisTemplate.delete(productKey);

        if( productDTO.getMainImageFile()!=null ) {
            ResponseResult rr2 = this.fileUploadApi.upload(new MultipartFile[]{productDTO.getMainImageFile()});
            if (rr2.getCode() == 0) {
                // 部分成功 - data是拼接的字符串
                String resultData = rr2.getData().toString();
                // 解析出URL部分（假设格式为"url,url失败:xxx"）
                String[] parts = resultData.split("失败:");
                if (parts.length > 0 && !parts[0].isEmpty()) {
                    String[] urls = parts[0].split(",");
                    if (urls.length > 0) {
                        productDTO.setMainImage(urls[0]); // 取第一个URL
                    }
                }
                // 抛出异常包含失败信息
                throw new RuntimeException("图片上传部分失败: " + (parts.length > 1 ? parts[1] : "未知原因"));
            } else if (rr2.getCode()==2){
                // 全部失败 - data是失败信息字符串
                throw new RuntimeException("图片上传失败: " + rr2.getData());
            }
            List<String> mainImages = (List<String>) rr2.getData();
            productDTO.setMainImage(mainImages.get(0));
        }

        if( productDTO.getSubImageFiles()!=null && productDTO.getSubImageFiles().length>0) {
            // 假设subImages是逗号分隔的图片数据，这里需要循环上传
            MultipartFile[] subImageFiles = productDTO.getSubImageFiles();
            ResponseResult rr3 = this.fileUploadApi.upload(subImageFiles);
            if (rr3.getCode() == 0) {
                // 部分成功 - data是拼接的字符串
                String resultData = rr3.getData().toString();
                // 解析出URL部分（假设格式为"url,url失败:xxx"）
                String[] parts = resultData.split("失败:");
                if (parts.length > 0 && !parts[0].isEmpty()) {
                    String[] urls = parts[0].split(",");
                    if (urls.length > 0) {
                        productDTO.setMainImage(urls[0]); // 取第一个URL
                    }
                }
                // 抛出异常包含失败信息
                throw new RuntimeException("图片上传部分失败: " + (parts.length > 1 ? parts[1] : "未知原因"));
            } else if (rr3.getCode()==2){
                // 全部失败 - data是失败信息字符串
                throw new RuntimeException("图片上传失败: " + rr3.getData());
            }
            List<String> subImages= (List<String>) rr3.getData();
            StringBuilder uploadedSubImages = new StringBuilder();
            for (String imgData : subImages) {
                if (StringUtils.hasText(imgData)) {
                    uploadedSubImages.append(imgData).append(",");
                }
            }
            if (uploadedSubImages.length() > 0) {
                uploadedSubImages.setLength(uploadedSubImages.length() - 1); // 移除最后一个逗号
            }
            productDTO.setSubImages(uploadedSubImages.toString());
        }

        // 将DTO转换为实体
        Product product = new Product();
        BeanUtils.copyProperties(productDTO, product);
        product.setUpdateTime(LocalDateTime.now());
        // 执行更新，MyBatis Plus会根据实体中的@TableId注解自动识别主键
        int result = productMapper.updateById(product);
        if (result > 0) {
            // 延迟双删：延迟一段时间后再次删除缓存
            executor.execute(() -> {
                try {
                    // 延迟1秒再次删除缓存
                    Thread.sleep(1000);
                    stringRedisTemplate.delete(productKey);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });

            // 通过Kafka发送消息
            kafkaProducterUtil.sendMessage("es_data_topic","products", jsonUtil.toJson(product));

            // 更新成功后，返回最新的商品信息
            return getProductById(productDTO.getProductId());
        } else {
            throw new RuntimeException("商品更新失败，商品ID: " + productDTO.getProductId());
        }
    }
    /**
     * 根据商品ID获取商品详情
     * @param productId 商品ID
     * @return 商品DTO
     */
    @Override
    public ProductDTO getProductById(Long productId) {
        // 从缓存中获取商品信息
        String productKey = RedisKeys.PRODUCT_INFO + productId;
        Product product=null;
        ProductDTO productDTO = new ProductDTO();
        String pJson = (String) stringRedisTemplate.opsForValue().get(productKey);
        if (pJson != null) {
            product = jsonUtil.fromJson(pJson,Product.class);
            BeanUtils.copyProperties(product,productDTO);
            log.info("从缓存中获取商品信息成功，商品ID: {}", productId);
            return productDTO;
        }
        productDTO = productMapper.getProductById(productId);
        // 缓存商品信息
        if (productDTO != null) {
            BeanUtils.copyProperties(productDTO,product);
            String json = jsonUtil.toJson(product);
            stringRedisTemplate.opsForValue().set(productKey, json);
            return productDTO;
        }
        return null;
    }

    /**
     * 根据关键词查找商品
     * @param keyword 关键词
     * @return 商品列表
     */
    @Override
    public List<ProductDTO> searchProducts(String keyword) {
        try {
            // 构建Elasticsearch查询
            SearchResponse<ProductDTO> response = elasticsearchClient.search(s -> s
                            .index("products")
                            .query(q -> q
                                    .bool(b -> b
                                            .must(mu -> mu
                                                    .multiMatch(m -> m
                                                            .query(keyword)
                                                            .fields("productName^3", "categoryName^2", "detail^1") // 商品名称权重更高
                                                    )
                                            )
                                            .filter(f -> f
                                                    .term(t -> t
                                                            .field("productStatus") // 商品状态字段
                                                            .value(1) // 1表示上架状态
                                                    )
                                            )
                                    )
                            )
                            .from(0)
                            .size(1000),
                    ProductDTO.class
            );
            // 处理搜索结果
            return response.hits().hits().stream()
                    .map(hit -> {
                        ProductDTO product = hit.source();
                        // 处理高亮结果
                        if (hit.highlight() != null) {
                            if (hit.highlight().containsKey("productName")) {
                                product.setProductName(hit.highlight().get("productName").get(0));
                            }
                            if (hit.highlight().containsKey("detail")) {
                                product.setDetail(hit.highlight().get("detail").get(0));
                            }
                            if (hit.highlight().containsKey("categoryName")) {
                                product.setCategoryName(hit.highlight().get("categoryName").get(0));
                            }
                        }
                        return product;
                    })
                    .collect(Collectors.toList());
        } catch (IOException e) {
            log.error("商品搜索失败: " + e.getMessage());
            throw new RuntimeException("商品搜索失败", e);
        }
    }

    /**
     * 添加商品浏览记录
     * @param userId
     * @param productId
     */
    @Override
    public void addProductView(Long userId, String productId) {
        String key = RedisKeys.USER_VIEWS + userId;
        String p=String.valueOf(productId);
        // 使用 Redis 的 LPUSH 命令将商品ID添加到列表的头部
        stringRedisTemplate.opsForList().leftPush(key, p);
        // 限制列表长度，保留最新的100条记录
        stringRedisTemplate.opsForList().trim(key, 0, 99);
        // 设置过期时间，例如30天
        stringRedisTemplate.expire(key, Duration.ofDays(30));

        // 加入商品被哪些用户浏览的ID
        stringRedisTemplate.opsForSet().add(RedisKeys.PRODUCT_RECOMMEND+productId+"", String.valueOf(userId));
    }

}
